﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved All Rights Reserved All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Linq;
using System.ServiceModel;
using System.ComponentModel;
using System.Reflection;
using System.Threading;
using FinPlusBaseCore;
using FinPlusAssembler;
using FinPlusInterfaces;

namespace FinPlusConnData
{
    public class ServiceConn
    {
        public IDPods DPods { get; private set; }
        public IPods Pods { get; private set; }
        public string LinkedId { get; private set; }
        public bool HasCompleted { get; set; }

        private IFinPlusServices _service;
        private NetTcpBinding _binding;
        private InstanceContext _context;
        private RequestCallback _callback;
        private readonly string _config, _user, _serviceKey, _serviceType, _query;
        private readonly bool _transpose, _isAsync;
        private int _asyncWait, _asyncRetry;
        private Env _env;

        //contruct
        public ServiceConn() {Initialise();}

        public ServiceConn(Env env, string config, string user, string serviceKey, string serviceType, string query, bool transpose, bool isAsync)
        {
            _env = env;
            _config = config;
            _user = user;
            _serviceKey = serviceKey;
            _serviceType = serviceType;
            _query = query;
            _transpose = transpose;
            _isAsync = isAsync;
            LinkedId = Guids.New();

            Initialise();
        }

        //public
        public void Start()
        {
            try
            {
                _service.GetPods(_env, _user, _config, _serviceKey, _serviceType, _query, LinkedId);
                if (!_isAsync)
                    SpinWait.SpinUntil(() => HasCompleted, _asyncRetry);
            }
            catch (FaultException<IFinPlusServices> e)
            {
                Level.Error.Log(e.Message, e);
            }
        }

        public void Save(IPod value, ChangeType changeType)
        {
            try
            {
                _service.SavePod(_env, _user, _config, _serviceKey, value.Cast<Bean>(), changeType, LinkedId);
            }
            catch (FaultException<IFinPlusServices> e)
            {
                Level.Error.Log(e.Message, e);
            }
        }

        public void Dispose()
        {
            try
            {
                if (_service != null)
                    _service.Dispose(_env, _user, _config, _serviceKey, LinkedId);
            }
            catch (Exception e)
            {
                Level.Error.Log(e.Message, e);
            }
        }

        public void Update(int id, IBean value, ChangeType changeType)
        {
            try
            {
                if (_transpose)
                {
                    IDPod dPod;
                    if (DPods.TryGet(id.ToString(), out dPod))
                        if (dPod.GetTimeStamp(value.Name) < value.TimeStamp)
                            dPod.SetBean(value);
                }
                else if (Pods.ContainsKey(id))
                {
                    var pods = Pods[id];
                    var bean = pods.Get(value.Name);
                    if (bean == null)
                    {
                        pods.TryAdd(value);
                        value.Update(value.Object, value.TimeStamp, value.User);
                    }
                    else if (bean.TimeStamp < value.TimeStamp)
                        bean.Update(value.Object, value.TimeStamp, value.User);
                }
            }
            catch (Exception e)
            {
                Level.Error.Log(e.Message);
            }
        }

        public void Remove(int id)
        {
            IPod f = null;
            if (_transpose)
                DPods.Remove(id.ToString());
            else if (Pods.ContainsKey(id))
                Pods.TryRemove(id, out f);
        }

        public void Add(IPod value)
        {
            if (_transpose)
            {
                var dPod = new DPod(value, _serviceKey, _user);
                DPods.Add(dPod);
                dPod.PropertyChanged += Bean_Updated;
            }
            else
            {
                value.ServiceKey = _serviceKey;
                Pods[value.Id] = value;
                value.BeanUpdate += Bean_Updated;
            }
        }

        public IPods Infos()
        {
           var pods = IOC.New<IPods>();
           var infos = _service.Infos();
           foreach(var info in infos)
               pods.Add(IOC.New<IPod>(info));

           return pods; 
        }

        public bool Stop(string serviceKey)
        {
            return _service.Stop(serviceKey);
        }

        public bool Start(string serviceKey)
        {
            return _service.Start(serviceKey);
        }

        public void Initialise()
        {
            _callback = new RequestCallback(this);
            _context = new InstanceContext(_callback);

            var config = Assembly.GetExecutingAssembly().ConfigPath().ConfigXml(out _env);
            var conn = config["connections"];

            if (_isAsync)
            {
                _asyncWait = (int)(double.Parse(conn["asyncwait"]) * 1000);
                _asyncRetry = int.Parse(conn["asyncretry"]);
            }

            _binding = new NetTcpBinding()
            {
                ReceiveTimeout = TimeSpan.FromHours(double.Parse(conn["receivetimeout"].ToString())),
                OpenTimeout = TimeSpan.FromHours(double.Parse(conn["opentimeout"])),
                CloseTimeout = TimeSpan.FromHours(double.Parse(conn["closetimeout"])),
                SendTimeout = TimeSpan.FromHours(double.Parse(conn["sendtimeout"]))
            };

            var factory = new DuplexChannelFactory<IFinPlusServices>(_context, _binding, conn["baseaddress"]);
            _service = factory.CreateChannel();

            if (_transpose)
                DPods = new DPods();
            else
                Pods = new Pods() { PodUpdate = Pod_Updated };
        }

        //private
        private void Pod_Updated(object s, BeanEventArgs a)
        {
            var pod = (Pod)s;
            pod.ServiceKey = _serviceKey;

            if (a.Bean == null)
                Save(pod, a.ChangeType);
        }

        private void Bean_Updated(object s, BeanEventArgs a)
        {
            var pod = (IPod)s;

            _service.AlterPod(_env, _user, _config, pod.ServiceKey, a.Bean.TimeStamp, pod.Name, pod.Id, a.Bean.Name, a.Bean.ToString());
        }

        private void Bean_Updated(object s, PropertyChangedEventArgs a)
        {
            try
            {
                var dPod = (IDPod)s;
                object Type, id, value;
                if (dPod.TryGetObject(PodField.Type.ToString(), out Type) && dPod.TryGetObject(PodField.Id.ToString(), out id) && dPod.TryGetObject(a.PropertyName, out value))
                    _service.AlterPod(_env, _user, _config, dPod.ServiceKey(), dPod.GetTimeStamp(a.PropertyName), (string)Type, (int)id, a.PropertyName, value.GetType().IsPrimitive ? value : value.ToString());
            }
            catch (Exception e)
            {
                Level.Debug.Log(e.Message, e);
            }
        }
    }
}
